﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;

using ProjectBase.Core;
using ProjectBase.Core.Web;

namespace $safeprojectname$.Controllers
{
    /// <summary>
    ///     Extension for AOP.
    /// </summary>
    public class AopUrlHelper : UrlHelper, IUrlHelper
    {
        #region const
        /// <summary>
        ///     Contains value "__RouteValuePrefix_".
        ///     This is expected to be used when extending
        ///     RouteData.Values[key] = value;
        ///     to distinguish that the value should be handled via AopUrlHelpr use:
        ///     RouteData.Values[AopUrlHelper.RouteValuePrefix + key] = value
        /// </summary>
        public const string RouteValuePrefix = "__RouteValuePrefix_";
        #endregion const

        #region constructor
        /// <summary>
        ///     Extends the base with collection 'CommonRouteData', which are applied to every resulting string.
        ///     (all links etc. can contain value set in the AOP)
        ///     Initializes a new instance of the System.Web.Mvc.UrlHelper class.
        /// </summary>
        /// <param name = "requestContext">An object that contains information about the current request and the defined route it matched.</param>
        public AopUrlHelper(RequestContext requestContext)
            : base(requestContext) { }

        /// <summary>
        ///     Extends the base with collection 'CommonRouteData', which are applied to every resulting string.
        ///     (all links etc. can contain value set in the AOP)
        ///     Initializes a new instance of the System.Web.Mvc.UrlHelper class.
        /// </summary>
        /// <param name = "requestContext">An object that contains information about the current request and the defined route it matched.</param>
        /// <param name = "routeCollection">A collection of System.Web.Routing instances.</param>
        public AopUrlHelper(RequestContext requestContext, RouteCollection routeCollection)
            : base(requestContext, routeCollection) { }
        #endregion constructor

        #region new (override)
        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName)
        {
            return base.Action(actionName, null, MergeRouteValues(null));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "routeValues"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, object routeValues)
        {
            return base.Action(actionName, null, MergeRouteValues(routeValues));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "controllerName"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, string controllerName)
        {
            return base.Action(actionName, controllerName, MergeRouteValues(null));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "routeValues"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, RouteValueDictionary routeValues)
        {
            return base.Action(actionName, null, MergeRouteValues(routeValues));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "controllerName"></param>
        /// <param name = "routeValues"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, string controllerName, object routeValues)
        {
            return base.Action(actionName, controllerName, MergeRouteValues(routeValues));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "controllerName"></param>
        /// <param name = "routeValues"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues)
        {
            return base.Action(actionName, controllerName, MergeRouteValues(routeValues));
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "controllerName"></param>
        /// <param name = "routeValues"></param>
        /// <param name = "protocol"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, string controllerName, object routeValues, string protocol)
        {
            return Action(actionName, controllerName, MergeRouteValues(routeValues), protocol, null);
        }

        /// <summary>
        ///     Injects 'page specific' parameters into every URL link.
        ///     That could be used for AOP, to keep some settings (e.g. the MasterPage)
        /// </summary>
        /// <param name = "actionName"></param>
        /// <param name = "controllerName"></param>
        /// <param name = "routeValues"></param>
        /// <param name = "protocol"></param>
        /// <param name = "hostName"></param>
        /// <returns></returns>
        public new virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues,
            string protocol, string hostName)
        {
            return base.Action(actionName, controllerName, MergeRouteValues(routeValues), protocol, hostName);
        }
        #endregion new (override)


        #region protected
        /// <summary>
        ///     In fact this is extender.
        ///     The route values (provided via Url.Action())
        ///     are extended with some 'page specific' AOP settings.
        ///     Any link in the document therefore contains that setting (e.g. MasterPage).
        /// </summary>
        /// <param name = "routeValues"></param>
        /// <returns></returns>
        protected virtual RouteValueDictionary MergeRouteValues(object routeValues)
        {
            // I. let's create starting collection of route values
            var dictionary = routeValues as IDictionary<string, object>;
            if (dictionary.IsNull())
            {
                dictionary = routeValues.Is()
                                 ? new RouteValueDictionary(routeValues)
                                 : new RouteValueDictionary();
            }

            // II. find out extenders
            var list = RequestContext.RouteData.Values
                .Union(dictionary)
                .Where(p => p.Key.StartsWith(RouteValuePrefix, StringComparison.Ordinal))
                ;

            // III. the result containing user values
            var result =
                new Dictionary<string, object>(dictionary)
                .Where(p => !p.Key.StartsWith(RouteValuePrefix, StringComparison.Ordinal))
                .ToDictionary(p => p.Key, p => p.Value);

            foreach (var pair in list)
            {
                // IV. need to remove the unique PREFIX idnetifier, used for this case
                var key = pair.Key.Replace(RouteValuePrefix, string.Empty);
                object value;
                if (!result.TryGetValue(key, out value))
                {
                    // IV. not exsiting is appended to result
                    result[key] = pair.Value;
                }
            }

            return new RouteValueDictionary(result);
        }
        #endregion protected
    }
}